<?php
class PopServer{
	private $hostname=""; 			// POP主机名
	private $username="";
	private $password="";
	private $port=110; 				// 主机的POP3端口，一般是110号端口
	private $timeout=5;				// 连接主机的最大超时时间
	private $connection=0;			// 保存与主机的连接
	private $state="DISCONNECTED"; 	// 保存当前的状态
	private $debug=1;				// 做为标识，是否在调试状态，是的话，输出调试信息
	private $err_str='';			// 如果出错，这里保存错误信息
	private $err_no;				// 如果出错，这里保存错误号码
	private $resp; 					// 临时保存服务器的响应信息 
	private $apop; 					// 指示需要使用加密方式进行密码验证，一般服务器不需要
	//邮件属性
	private $size; 					// 各邮件的总大小
	private $mail_count; 			// 邮件数
	private $mail_list; 			// 一个数组，保存各个邮件的大小及其在邮件服务器上序号 array(num=>0,size=>0)
	//邮件内容
	private $head=array(); 			// 邮件头的内容，数组
	private $body=array(); 			// 邮件体的内容，数组  
	
	
	public function PopServer($server,$username,$password,$port=110,$time_out=5,$debug=0){
		$this->hostname=$server;
		$this->username=$username;
		$this->password=$password;
		$this->port=$port;
		$this->timeout=$time_out;
		$this->debug=$debug;
		return true;
	}
	
	public function open(){
		if($this->hostname==""){
			$this->err_str="无效的主机名!!";
			return false;
		}
		if ($this->debug) echo "正在打开$this->hostname,$this->port,$this->err_no, $this->err_str, $this->timeout<br/>";
		if (!$this->connection=fsockopen($this->hostname,$this->port,&$this->err_no, &$this->err_str, $this->timeout)){
			$this->err_str="连接到POP服务器失败，错误信息：".$this->err_str."错误号：".$this->err_no;
			return false;
		}else{
			$this->getresp();
			if($this->debug)
				$this->outdebug($this->resp);
			if (substr($this->resp,0,3)!="+OK"){
				$this->err_str="服务器返回无效的信息：".$this->resp."请检查POP服务器是否正确";
				return false;
			}
			$this->state="AUTHORIZATION";
			return true;
		}
	}
	
	public function close(){
		if($this->connection!=0)
		{
			if($this->state=="TRANSACTION")
				$this->command("QUIT",3,"+OK");
			fclose($this->connection);
			$this->connection=0;
			$this->state="DISCONNECTED";
		}
	}
	
	public function getresp(){
		for($this->resp="";;){
			if(feof($this->connection))
				return false;
			$this->resp.=fgets($this->connection,100);
			$length=strlen($this->resp);
			if($length>=2 && substr($this->resp,$length-2,2)=="\r\n"){
				$this->resp=strtok($this->resp,"\r\n");
				return true;
			}
		}
	}
	
	public function outdebug($message){
		echo htmlspecialchars($message)."<br/>\n";
	}
	
	public function command($command,$return_lenth=1,$return_code='+'){
		if($this->connection==0)
		{
			$this->err_str="没有连接到任何服务器，请检查网络连接";
			return false;
		}
		if ($this->debug)
			$this->outdebug(">>> $command");
		if (!fputs($this->connection,"$command\r\n"))
		{
			$this->err_str="无法发送命令".$command; 
			return false;
		}else{
			$this->getresp();
			if($this->debug)
				$this->outdebug($this->resp);
			if(substr($this->resp,0,$return_lenth)!=$return_code){
				$this->err_str=$command." 命令服务器返回无效:".$this->resp;
				return false;
			}else 
				return true; 
		} 
	}  

	public function Login(){//发送用户名及密码，登录到服务器
		if($this->state!="AUTHORIZATION"){
			$this->err_str="还没有连接到服务器或状态不对";
			return false;
		}
		if(!$this->apop) //服务器是否采用APOP用户认证
		{
			if(!$this->command("USER $this->username",3,"+OK")){
				$this->err_str="邮箱用户名不存在！";
				return false;
			}
			if(!$this->command("PASS $this->password",3,"+OK")){
				$this->err_str="邮箱密码错误！";
				return false;
			}
		}else{
			//echo $this->resp=strtok($this->resp,"\r\n");
			if (!$this->command("APOP $user ".md5($this->greeting.$password),3,"+OK")){
				$this->err_str="邮箱密码错误！";
				return false;
			} 
		}
		$this->state="TRANSACTION"; // 用户认证通过，进入传送模式
		return true;
	}
	
	/**
	 * 对应着stat命令，取得总的邮件数与总的大小
	 * 
	 */
	public function stat(){
		if($this->state!="TRANSACTION"){
			$this->err_str="还没有连接到服务器或没有成功登录";
			return false;
		}
		if (!$this->command("STAT",3,"+OK"))
			return false;
		else{ 
			$this->resp=strtok($this->resp," ");
			$this->mail_count=strtok(" "); // 取得邮件总数
			$this->size=strtok(" "); //取得总的字节大小
			return true;
		} 
	} 
	/**
	 * 对应的是LIST命令，取得每个邮件的大小及序号。一般来说用到的是List命令，如果指定了$uni_id ，
	 * 则使用UIDL命令，返回的是每个邮件的标识符，事实上，这个标识符一般是没有什么用的。
	 * 取得的各个邮件的大小返回到类的内部变量mail_list这个二维数组里。
	 * 
	 * @param $mess
	 * @param $uni_id
	 */
	public function listmail($mess=null,$uni_id=null) {
		if($this->state!="TRANSACTION"){
			$this->err_str="还没有连接到服务器或没有成功登录";
			return false;
		}
		if($uni_id)
			$command="UIDL ";
		else 
			$command="LIST "; 
		if ($mess)
			$command.=$mess;
		if (!$this->command($command,3,"+OK")){
		//echo $this->err_str;
			return false;
		}else{
			$i=0;
			$this->mail_list=array();
			$this->getresp();
			while($this->resp!="."){
				$i++;
				if ($this->debug){
					$this->outdebug($this->resp);
				}
				if($uni_id)
				{
					$this->mail_list[$i]["num"]=strtok($this->resp," ");
					$this->mail_list[$i]["size"]=strtok(" ");
				}else{
					$this->mail_list[$i]["num"]=intval(strtok($this->resp," "));
					$this->mail_list[$i]["size"]=intval(strtok(" "));
				}
				$this->getresp();
			}
			return true;
		} 
	}
	/**
	 * 取得邮件的内容，$num是邮件的序号，$line是指定共取得正文的多少行。
	 * 有些时候，如邮件比较大而我们只想先查看邮件的主题时是必须指定行数的。
	 * 默认值$line=-1，即取回所有的邮件内容，取得的内容存放到内部变量$head，$body两个数组里，
	 * 数组里的每一个元素对应的是邮件源代码的一行。
	 * 
	 * @param $num
	 * @param $line
	 */
	public function getmail($num=1,$line=-1){
		if($this->state!="TRANSACTION"){
			$this->err_str="不能收取信件，还没有连接到服务器或没有成功登录";
			return false;
		}
		if($line<0)
			$command="retr $num";
		else
			$command="top $num $line";
		if(!$this->command("$command",3,"+OK"))
			return false;
		else{
			$this->getresp();
			$is_head=true;
			while($this->resp!="."){ // . 号是邮件结束的标识
				if ($this->debug) 
					$this->outdebug($this->resp);
				if (substr($this->resp,0,1)==".")
					$this->resp=substr($this->resp,1,strlen($this->resp)-1);
				if (trim($this->resp)=="") // 邮件头与正文部分的是一个空行
					$is_head=false;
				if($is_head)
					$this->head[]=$this->resp;
				else{
					$this->body[]=$this->resp;
				}
				$this->getresp();
			}
			return true;
		}
	}
	
	/**
	 * 删除指定序号的邮件，$num 是服务器上的邮件序号
	 * 
	 * @param $num
	 */
	public function delete($num){
		if($this->state!="TRANSACTION"){
			$this->err_str="不能删除远程信件，还没有连接到服务器或没有成功登录";
			return false;
		}
		if(!$num){
			$this->err_str="删除的参数不对";
			return false;
		}
		if ($this->command("DELE $num ",3,"+OK"))
			return true;
		else 
			return false;
	}
	
	

	public function __getErr_str(){
		return $this->err_str;
	}
	public function __getState(){
		return $this->state;
	}
	public function __getMail_count(){
		return $this->mail_count;
	}
	public function __getSize(){
		return $this->size;
	}
	public function __getResp(){
		return $this->resp;
	}
	public function __getMail_list(){
		return $this->mail_list;	
	}
	public function __getHead(){
		return $this->head;
	}
	public function __getBody(){
		return $this->body;
	}
}
?>